{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "1f47245d",
   "metadata": {},
   "source": [
    "<p align=\"center\">\n",
    "    <img src=\"https://github.com/GeostatsGuy/GeostatsPy/blob/master/TCG_color_logo.png?raw=true\" width=\"220\" height=\"240\" />\n",
    "\n",
    "</p>\n",
    "\n",
    "## Interactive Convolution and k-Nearest Neighbours Regression\n",
    "\n",
    "#### Michael J. Pyrcz, Professor, The University of Texas at Austin \n",
    "\n",
    "##### [Twitter](https://twitter.com/geostatsguy) | [GitHub](https://github.com/GeostatsGuy) | [Website](http://michaelpyrcz.com) | [GoogleScholar](https://scholar.google.com/citations?user=QVZ20eQAAAAJ&hl=en&oi=ao) | [Book](https://www.amazon.com/Geostatistical-Reservoir-Modeling-Michael-Pyrcz/dp/0199731446) | [YouTube](https://www.youtube.com/channel/UCLqEr-xV-ceHdXXXrTId5ig)  | [LinkedIn](https://www.linkedin.com/in/michael-pyrcz-61a648a1)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2d66eb59",
   "metadata": {},
   "source": [
    "Here's a set of interactive dashboards to explore k-Nearest Neighbours-based predictive machine learning models. Since it is useful to start from convolution, my first dash board is a simpel convolution regression model. Then I switch from convolution with a set window size to k-nearest neighbours with addaptive window size scaling based on local data density. I also show the difference between exhaustive and sparsely sampled cases.\n",
    "\n",
    "To assist you with background content, I have a lectures available with linked interactive codes and well-documented workflows in Python:\n",
    "\n",
    "* [k-Nearest Neighbours Regression](https://youtu.be/lzmeChSYvv8?si=EBxfAfSt3MD8tV_1) \n",
    "* [k-Nearest Neighbours Considerations](https://youtu.be/Zw1WAH6s5yg?si=1MiR-x8jY3wer5v6) \n",
    "\n",
    "I also have lectures on:\n",
    "\n",
    "* [Machine Learning Basics](https://youtu.be/zOUM_AnI1DQ?si=L1FxPRc-n9y8Yuk6)\n",
    "* [Machine Learning Model Generalization and Overfit](https://youtu.be/GGoNTMrCBbk?si=itx1p3G6PG7witpe)\n",
    "* [Machine Learning Model Norms](https://youtu.be/JmxGlrurQp0?si=FPC7-Et66bWRMhFl)\n",
    "    \n",
    "these are all part of my [Machine Learning](https://www.youtube.com/playlist?list=PLG19vXLQHvSC2ZKFIkgVpI9fCjkN38kwf) course. Note, all recorded lectures, interactive and well-documented workflow demononstrations are available on my GitHub repository [GeostatsGuy's Python Numerical Demos](https://github.com/GeostatsGuy/PythonNumericalDemos). \n",
    "\n",
    "#### k-Nearest Neighbours Regression\n",
    "\n",
    "TBD\n",
    "\n",
    "#### Other Resources\n",
    "\n",
    "This is a tutorial / demonstration of **k-Nearest Neighbours Regression**.  In $Python$, the $SciPy$ package, specifically the $Stats$ functions (https://docs.scipy.org/doc/scipy/reference/stats.html) provide excellent tools for efficient use of statistics.  \n",
    "I have previously provided this example in R and posted it on GitHub:\n",
    "\n",
    "#### Getting Started\n",
    "\n",
    "Here's the steps to get setup in Python with the GeostatsPy package:\n",
    "\n",
    "1. Install Anaconda 3 on your machine (https://www.anaconda.com/download/). \n",
    "2. From Anaconda Navigator (within Anaconda3 group), go to the environment tab, click on base (root) green arrow and open a terminal. \n",
    "3. In the terminal type: pip install geostatspy. \n",
    "4. Open Jupyter and in the top block get started by copy and pasting the code block below from this Jupyter Notebook to start using the geostatspy functionality. \n",
    "\n",
    "#### Import Required Packages\n",
    "\n",
    "Let's import the GeostatsPy package."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "e8108e46",
   "metadata": {},
   "outputs": [],
   "source": [
    "supress_warnings = True\n",
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "from matplotlib.ticker import (MultipleLocator, AutoMinorLocator) # control of axes ticks\n",
    "from matplotlib.ticker import (MultipleLocator, \n",
    "                               FormatStrFormatter, \n",
    "                               AutoMinorLocator) \n",
    "from ipywidgets import interactive                          # widgets and interactivity\n",
    "from ipywidgets import widgets                            \n",
    "from ipywidgets import Layout\n",
    "from ipywidgets import Label\n",
    "from ipywidgets import VBox, HBox\n",
    "import heapq\n",
    "cmap = plt.cm.inferno                                       # default color bar, no bias and friendly for color vision defeciency\n",
    "plt.rc('axes', axisbelow=True)                              # grid behind plotting elements\n",
    "seed = 73073                                                # random number seed\n",
    "if supress_warnings == True:\n",
    "    import warnings                                         # supress any warnings for this demonstration\n",
    "    warnings.filterwarnings('ignore')  \n",
    "cmap = plt.cm.inferno"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6e4f905c",
   "metadata": {},
   "source": [
    "If you get a package import error, you may have to first install some of these packages. This can usually be accomplished by opening up a command window on Windows and then typing 'python -m pip install [package-name]'. More assistance is available with the respective package docs. \n",
    "\n",
    "#### Declare functions\n",
    "\n",
    "Let's define a couple of functions to streamline plotting correlation matrices and visualization of a decision tree regression model. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "e0b7f21d",
   "metadata": {},
   "outputs": [],
   "source": [
    "def add_grid():\n",
    "    plt.gca().grid(True, which='major',linewidth = 1.0); plt.gca().grid(True, which='minor',linewidth = 0.2) # add y grids\n",
    "    plt.gca().tick_params(which='major',length=7); plt.gca().tick_params(which='minor', length=4)\n",
    "    plt.gca().xaxis.set_minor_locator(AutoMinorLocator()); plt.gca().yaxis.set_minor_locator(AutoMinorLocator()) # turn on minor ticks\n",
    "\n",
    "def findClosestElements(arr, k, x): # function from https://www.geeksforgeeks.org/find-k-closest-elements-given-value/, thank you!)\n",
    "    # Create a max heap to store the pairs of absolute differences and negative values\n",
    "    max_heap = []\n",
    " \n",
    "    for num in arr:\n",
    "        # Skip if the element is equal to x\n",
    "        if num == x:\n",
    "            continue\n",
    " \n",
    "        # Calculate the absolute difference and add the pair to the max heap\n",
    "        diff = abs(num - x)\n",
    "        heapq.heappush(max_heap, (-diff, num))\n",
    " \n",
    "        # If the size of the max heap exceeds k, remove the element with the maximum absolute difference\n",
    "        if len(max_heap) > k:\n",
    "            heapq.heappop(max_heap)\n",
    " \n",
    "    # Store the result in an array\n",
    "    result = []\n",
    " \n",
    "    # Retrieve the top k elements from the max heap\n",
    "    while max_heap:\n",
    "        # Get the top element from the max heap\n",
    "        diff, num = heapq.heappop(max_heap)\n",
    " \n",
    "        # Add the value to the result array\n",
    "        result.append(num)\n",
    " \n",
    "    # Return the closest numbers in ascending order\n",
    "    return sorted(result)  "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5a937c49",
   "metadata": {},
   "source": [
    "#### Interactive Dashboard - convolution-based regression in 1D\n",
    "\n",
    "Let's start by visualizing convolution-based regression in 1D"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "c9d31466",
   "metadata": {},
   "outputs": [],
   "source": [
    "l = widgets.Text(value='                                                     Convolution Regression Demo  -   Michael Pyrcz, Professor, The University of Texas at Austin',\n",
    "        layout=Layout(width='950px', height='30px'))\n",
    "# P_happening_label = widgets.Text(value='Probability of Happening',layout=Layout(width='50px',height='30px',line-size='0 px'))\n",
    "\n",
    "ftype = widgets.Dropdown(value='Uniform',options=['Uniform', 'Distance'], description='Wt.Shape:',\n",
    "        layout=Layout(width='300px',height='40px'))\n",
    "\n",
    "fs = widgets.IntSlider(min=1, max = 50, value=5, step = 1,description = ' Wt.Size:',orientation='horizontal', \n",
    "        style = {'description_width':'initial','button_color':'green'},layout=Layout(width='300px',height='40px'),\n",
    "        continuous_update=False,readout_format='.0f')\n",
    "\n",
    "psamp = widgets.FloatSlider(min=0.0, max = 1.0, value=0.2, step = 0.1,description = r'Sample Proportion:',orientation='horizontal', \n",
    "        style = {'description_width':'initial','button_color':'green'},layout=Layout(width='300px',height='40px'),\n",
    "        continuous_update=False,readout_format='.2f')\n",
    "\n",
    "ui_summary = widgets.HBox([ftype,fs,psamp],)\n",
    "ui_summary1 = widgets.VBox([l,ui_summary],)\n",
    "\n",
    "def run_plot_summary(ftype,fs,psamp):\n",
    "    gr = 10; seed = 13\n",
    "    \n",
    "    np.random.seed(seed=seed)\n",
    "    x = np.linspace(1,100,100)\n",
    "    g = np.convolve(np.random.normal(loc=10,scale=2,size=100+2*gr),np.ones(gr*2+1)/(gr*2+1),mode='same')[gr:-gr]\n",
    "    \n",
    "    f = np.ones(fs*2+1)\n",
    "    if ftype == 'Uniform':\n",
    "        f = f/(fs*2+1)\n",
    "    elif ftype == 'Distance':\n",
    "        for i in range(fs*2+1):\n",
    "            f[i] = abs(1/( (i - fs) +0.01))\n",
    "        f[fs] = 1.5; f = f/sum(f)\n",
    "        \n",
    "    # y = np.convolve(g,f,mode='full')\n",
    "    y = np.zeros(len(x))\n",
    "    for i in range(0,len(x)):\n",
    "        ssum = 0; count = 0; sum_wt = 0.0\n",
    "        for j in range(-fs,fs,1):\n",
    "            if i+j >= 0 and i+j < len(x):\n",
    "    #                 print(j, i, i+j)\n",
    "                ssum = ssum + g[i+j]*f[j+fs]\n",
    "                count = count + 1; sum_wt = sum_wt + f[j+fs]\n",
    "    #           print(f[j+fs],g[i+j])\n",
    "    #   print(sum_wt); print(ssum)\n",
    "        if(sum_wt > 0):\n",
    "            y[i] = ssum / (sum_wt)\n",
    "        else: \n",
    "            y[i] = np.NaN\n",
    "    #     print(i)\n",
    "    \n",
    "    y2 = np.zeros(len(x))\n",
    "    np.random.seed(seed=seed)\n",
    "    ind = np.where(np.random.rand(len(x)) < psamp, True, False)\n",
    "    \n",
    "    for i in range(0,len(x)):\n",
    "        ssum = 0; count = 0; sum_wt = 0.0\n",
    "        for j in range(-fs,fs,1):\n",
    "            if i+j >= 0 and i+j < len(x):\n",
    "                if ind[i+j]:\n",
    "    #                 print(j, i, i+j)\n",
    "                    ssum = ssum + g[i+j]*f[j+fs]\n",
    "                    count = count + 1; sum_wt = sum_wt + f[j+fs]\n",
    "    #                 print(f[j+fs],g[i+j])\n",
    "    #     print(sum_wt); print(ssum)\n",
    "        if(sum_wt > 0):\n",
    "            y2[i] = ssum / (sum_wt)\n",
    "        else: \n",
    "            y2[i] = np.NaN\n",
    "    #     print(y2[i])\n",
    "    \n",
    "    plt.subplot(131)\n",
    "    plt.plot(np.linspace(-fs,fs,fs*2+1),f,color='black',lw=2,label='Weighting Function, $f(\\Delta)$',zorder=100)\n",
    "    plt.plot([-fs,-fs],[0,f[0]],color='black',lw=2,zorder=100)\n",
    "    plt.plot([fs,fs],[0,f[len(f)-1]],color='black',lw=2,zorder=100)\n",
    "    plt.fill_between(np.linspace(-fs,fs,fs*2+1),np.zeros(fs*2+1),f,color='black',alpha=0.4,zorder=10)\n",
    "    plt.xlim([-100,100]); plt.ylim([0,np.max(f)*1.5])\n",
    "\n",
    "#    plt.gca().yaxis.set_major_locator(MultipleLocator(1)) \n",
    "    plt.gca().yaxis.set_major_formatter(FormatStrFormatter('% 1.3f')) \n",
    "    \n",
    "    \n",
    "    add_grid(); plt.legend(loc='upper left')\n",
    "    plt.xlabel(r'$\\Delta$ (m)'); plt.title('Weighting Function, $f(\\Delta)$')\n",
    "    \n",
    "    plt.subplot(132)\n",
    "    plt.plot(x,g,color='black',lw=2,label=r'Original Function, $g(x)$')\n",
    "    # plt.plot(x,y[fs:-fs],color='red',lw=2,label=r'Convolved Function, $(f * g)$')\n",
    "    plt.plot(x,y,color='red',lw=2,label=r'Convolved Function, $(f * g)$')\n",
    "    plt.xlim([1,100]); plt.ylim([8,12])\n",
    "    add_grid(); plt.legend(loc='upper left')\n",
    "    plt.xlabel(r'$x$ (m)'); plt.title('Original and Convolved Functions')\n",
    "    \n",
    "    plt.subplot(133)\n",
    "    plt.plot(x,g,color='black',alpha=0.3,lw=2,label=r'Original Function, $g(x)$')\n",
    "    plt.scatter(x[ind],g[ind],color='black',edgecolor='black',label=r'Sample Data',zorder=10)\n",
    "    plt.plot(x,y2,color='red',lw=2,label=r'Sparse Convolved Function, $(f * g)$')\n",
    "    plt.xlim([1,100]); plt.ylim([8,12])\n",
    "    add_grid(); plt.legend(loc='upper left')\n",
    "    plt.xlabel(r'$x$ (m)'); plt.title('Original and Sparse DataConvolved Functions')\n",
    "    \n",
    "    plt.subplots_adjust(left=0.0, bottom=0.0, right=2.0, top=1.2, wspace=0.2, hspace=0.1); plt.show()  \n",
    "        \n",
    "interactive_plot_summary = widgets.interactive_output(run_plot_summary, {'ftype':ftype,'fs':fs,'psamp':psamp})\n",
    "interactive_plot_summary.clear_output(wait = True)  "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "52149308",
   "metadata": {},
   "source": [
    "### Interactive Convolution-based Regression Demonstration\n",
    "\n",
    "* select weights 'Uniform' or 'Distance', weighting window size, & proportion of samples. Then compare the exhaustive & sparsely sampled cases.\n",
    "\n",
    "#### Michael Pyrcz, Professor, The University of Texas at Austin \n",
    "##### [Twitter](https://twitter.com/geostatsguy) | [GitHub](https://github.com/GeostatsGuy) | [Website](http://michaelpyrcz.com) | [GoogleScholar](https://scholar.google.com/citations?user=QVZ20eQAAAAJ&hl=en&oi=ao) | [Book](https://www.amazon.com/Geostatistical-Reservoir-Modeling-Michael-Pyrcz/dp/0199731446) | [YouTube](https://www.youtube.com/channel/UCLqEr-xV-ceHdXXXrTId5ig)  | [LinkedIn](https://www.linkedin.com/in/michael-pyrcz-61a648a1) | [GeostatsPy](https://github.com/GeostatsGuy/GeostatsPy)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "899a1108",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "30454b7933c54d689ca8da7bcb5a527f",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "VBox(children=(Text(value='                                                     Convolution Regression Demo  -…"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "527c28286075433abe4f219bd93eb699",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Output()"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "display(ui_summary1, interactive_plot_summary)                           # display the interactive plot"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d82deb79",
   "metadata": {},
   "source": [
    "#### Interactive Dashboard - k-Nearest Neighbours in 1D\n",
    "\n",
    "Let's start by visualizing the change in k-Nearest Neighbours with change in k and weighting function."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "0431ea7e",
   "metadata": {},
   "outputs": [],
   "source": [
    "l = widgets.Text(value='                                      k-Nearest Neighbours Regression Demo  -   Michael Pyrcz, Professor, The University of Texas at Austin',\n",
    "        layout=Layout(width='950px', height='30px'))\n",
    "# P_happening_label = widgets.Text(value='Probability of Happening',layout=Layout(width='50px',height='30px',line-size='0 px'))\n",
    "\n",
    "ftype = widgets.Dropdown(value='Uniform',options=['Uniform', 'Distance'], description='Wt.Shape:',\n",
    "        layout=Layout(width='300px',height='40px'))\n",
    "\n",
    "k = widgets.IntSlider(min=1, max = 100, value=5, step = 1,description = ' k:',orientation='horizontal', \n",
    "        style = {'description_width':'initial','button_color':'green'},layout=Layout(width='300px',height='40px'),\n",
    "        continuous_update=False,readout_format='.0f')\n",
    "\n",
    "psamp = widgets.FloatSlider(min=0.0, max = 1.0, value=0.2, step = 0.1,description = r'Sample Proportion:',orientation='horizontal', \n",
    "        style = {'description_width':'initial','button_color':'green'},layout=Layout(width='300px',height='40px'),\n",
    "        continuous_update=False,readout_format='.2f')\n",
    "\n",
    "ui_summary = widgets.HBox([ftype,k,psamp],)\n",
    "ui_summary2 = widgets.VBox([l,ui_summary],)\n",
    "\n",
    "def run_plot_summary2(ftype,k,psamp):\n",
    "    gr = 10; seed = 13; fs = 100\n",
    "    \n",
    "    np.random.seed(seed=seed)\n",
    "    x = np.linspace(1,100,100)\n",
    "    g = np.convolve(np.random.normal(loc=10,scale=2,size=100+2*gr),np.ones(gr*2+1)/(gr*2+1),mode='same')[gr:-gr]\n",
    "    \n",
    "    f = np.ones(fs*2+1)\n",
    "    if ftype == 'Uniform':\n",
    "        f = f/(fs*2+1)\n",
    "    elif ftype == 'Distance':\n",
    "        for i in range(fs*2+1):\n",
    "            f[i] = abs(1/( (i - fs) +0.01))\n",
    "        f[fs] = 1.5; f = f/sum(f)\n",
    "        \n",
    "    # y = np.convolve(g,f,mode='full')\n",
    "    y = np.zeros(len(x))\n",
    "    for i in range(0,len(x)):\n",
    "        ssum = 0; count = 0; sum_wt = 0.0\n",
    "        nearest = findClosestElements(x, k, i)\n",
    "#         print(nearest)\n",
    "        for j in nearest:\n",
    "            ssum = ssum + g[int(j-1)]*f[int(j-1)-i+fs]\n",
    "            count = count + 1; sum_wt = sum_wt + f[int(j-1)-i+fs]\n",
    "    #           print(f[j+fs],g[i+j])\n",
    "    #   print(sum_wt); print(ssum)\n",
    "        if(sum_wt > 0):\n",
    "            y[i] = ssum / (sum_wt)\n",
    "        else: \n",
    "            y[i] = np.NaN\n",
    "    #     print(i)\n",
    "    \n",
    "    y2 = np.zeros(len(x))\n",
    "    np.random.seed(seed=seed)\n",
    "    ind = np.where(np.random.rand(len(x)) < psamp, True, False)\n",
    "    xind = x[ind]\n",
    "    for i in range(0,len(x)):\n",
    "        ssum = 0; count = 0; sum_wt = 0.0\n",
    "        nearest_sp = findClosestElements(xind, k, i+0.01)\n",
    "#        print(nearest_sp)\n",
    "        for j in nearest_sp:\n",
    "            ssum = ssum + g[int(j-1)]*f[int(j-1)-i+fs]\n",
    "            count = count + 1; sum_wt = sum_wt + f[int(j-1)-i+fs]\n",
    "    #           print(f[j+fs],g[i+j])\n",
    "#         print(sum_wt); print(ssum)\n",
    "        if(sum_wt > 0):\n",
    "            y2[i] = ssum / (sum_wt)\n",
    "        else: \n",
    "            y2[i] = np.NaN\n",
    "    #     print(i)\n",
    "    \n",
    "#     for i in range(0,len(x)):\n",
    "#         ssum = 0; count = 0; sum_wt = 0.0\n",
    "#         for j in range(-fs,fs,1):\n",
    "#             if i+j >= 0 and i+j < len(x):\n",
    "#                 if ind[i+j]:\n",
    "#     #                 print(j, i, i+j)\n",
    "#                     ssum = ssum + g[i+j]*f[j+fs]\n",
    "#                     count = count + 1; sum_wt = sum_wt + f[j+fs]\n",
    "#     #                 print(f[j+fs],g[i+j])\n",
    "#     #     print(sum_wt); print(ssum)\n",
    "#         if(sum_wt > 0):\n",
    "#             y2[i] = ssum / (sum_wt)\n",
    "#         else: \n",
    "#             y2[i] = np.NaN\n",
    "#     #     print(y2[i])\n",
    "    \n",
    "    plt.subplot(131)\n",
    "    plt.plot(np.linspace(-fs,fs,fs*2+1),f,color='black',lw=2,label='Weighting Function, $f(\\Delta)$',zorder=100)\n",
    "    plt.plot([-fs,-fs],[0,f[0]],color='black',lw=2,zorder=100)\n",
    "    plt.plot([fs,fs],[0,f[len(f)-1]],color='black',lw=2,zorder=100)\n",
    "    plt.fill_between(np.linspace(-fs,fs,fs*2+1),np.zeros(fs*2+1),f,color='black',alpha=0.4,zorder=10)\n",
    "    plt.xlim([-100,100]); plt.ylim([0,np.max(f)*1.5])\n",
    "\n",
    "#    plt.gca().yaxis.set_major_locator(MultipleLocator(1)) \n",
    "    plt.gca().yaxis.set_major_formatter(FormatStrFormatter('% 1.3f')) \n",
    "    \n",
    "    \n",
    "    add_grid(); plt.legend(loc='upper left')\n",
    "    plt.xlabel(r'$\\Delta$ (m)'); plt.title('Weighting Function, $f(\\Delta)$')\n",
    "    \n",
    "    plt.subplot(132)\n",
    "    plt.plot(x,g,color='black',lw=2,label=r'Original Function, $g(x)$')\n",
    "    # plt.plot(x,y[fs:-fs],color='red',lw=2,label=r'Convolved Function, $(f * g)$')\n",
    "    plt.plot(x,y,color='red',lw=2,label=r'Convolved Function, $(f * g)$')\n",
    "    plt.xlim([1,100]); plt.ylim([8,12])\n",
    "    add_grid(); plt.legend(loc='upper left')\n",
    "    plt.xlabel(r'$x$ (m)'); plt.title('Original and Convolved Functions')\n",
    "    \n",
    "    plt.subplot(133)\n",
    "    plt.plot(x,g,color='black',alpha=0.3,lw=2,label=r'Original Function, $g(x)$')\n",
    "    plt.scatter(x[ind],g[ind],color='black',edgecolor='black',label=r'Sample Data',zorder=10)\n",
    "    plt.plot(x,y2,color='red',lw=2,label=r'Sparse Convolved Function, $(f * g)$')\n",
    "    plt.xlim([1,100]); plt.ylim([8,12])\n",
    "    add_grid(); plt.legend(loc='upper left')\n",
    "    plt.xlabel(r'$x$ (m)'); plt.title('Original and Sparse DataConvolved Functions')\n",
    "    \n",
    "    plt.subplots_adjust(left=0.0, bottom=0.0, right=2.0, top=1.2, wspace=0.2, hspace=0.1); plt.show()  \n",
    "        \n",
    "interactive_plot_summary2 = widgets.interactive_output(run_plot_summary2, {'ftype':ftype,'k':k,'psamp':psamp})\n",
    "interactive_plot_summary2.clear_output(wait = True)  "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7e2df561",
   "metadata": {},
   "source": [
    "### Interactive k-Nearest Neighbours Regression Demonstration\n",
    "\n",
    "* select weights 'Uniform' or 'Distance', k, & proportion of samples. Then compare the exhaustive & sparsely sampled cases.\n",
    "\n",
    "#### Michael Pyrcz, Professor, The University of Texas at Austin \n",
    "##### [Twitter](https://twitter.com/geostatsguy) | [GitHub](https://github.com/GeostatsGuy) | [Website](http://michaelpyrcz.com) | [GoogleScholar](https://scholar.google.com/citations?user=QVZ20eQAAAAJ&hl=en&oi=ao) | [Book](https://www.amazon.com/Geostatistical-Reservoir-Modeling-Michael-Pyrcz/dp/0199731446) | [YouTube](https://www.youtube.com/channel/UCLqEr-xV-ceHdXXXrTId5ig)  | [LinkedIn](https://www.linkedin.com/in/michael-pyrcz-61a648a1) | [GeostatsPy](https://github.com/GeostatsGuy/GeostatsPy)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "adfe7d9f",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "66c9eaabaa2640f5b3384518d50a522f",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "VBox(children=(Text(value='                                      k-Nearest Neighbours Regression Demo  -   Mic…"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "3c21b49fa9014a86a7ef46f516c329c0",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Output()"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "display(ui_summary2, interactive_plot_summary2)                           # display the interactive plot"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "749693c2",
   "metadata": {},
   "source": [
    "#### Comments\n",
    "\n",
    "These interactive dashboards were designed as an educational tool to assist in learning about convolution and specifically k-nearest neighbours predictive machine learning. The dashboards include:\n",
    "\n",
    "TBD\n",
    "\n",
    "\n",
    "I hope this is helpful,\n",
    "\n",
    "*Michael*\n",
    "\n",
    "Michael Pyrcz, Ph.D., P.Eng. Professor The Hildebrand Department of Petroleum and Geosystems Engineering, Bureau of Economic Geology, The Jackson School of Geosciences, The University of Texas at Austin\n",
    "On twitter I'm the @GeostatsGuy.\n",
    "\n",
    "***\n",
    "\n",
    "#### More on Michael Pyrcz and the Texas Center for Geostatistics:\n",
    "\n",
    "### Michael Pyrcz, Professor, University of Texas at Austin \n",
    "*Novel Data Analytics, Geostatistics and Machine Learning Subsurface Solutions*\n",
    "\n",
    "With over 17 years of experience in subsurface consulting, research and development, Michael has returned to academia driven by his passion for teaching and enthusiasm for enhancing engineers' and geoscientists' impact in subsurface resource development. \n",
    "\n",
    "For more about Michael check out these links:\n",
    "\n",
    "#### [Twitter](https://twitter.com/geostatsguy) | [GitHub](https://github.com/GeostatsGuy) | [Website](http://michaelpyrcz.com) | [GoogleScholar](https://scholar.google.com/citations?user=QVZ20eQAAAAJ&hl=en&oi=ao) | [Book](https://www.amazon.com/Geostatistical-Reservoir-Modeling-Michael-Pyrcz/dp/0199731446) | [YouTube](https://www.youtube.com/channel/UCLqEr-xV-ceHdXXXrTId5ig)  | [LinkedIn](https://www.linkedin.com/in/michael-pyrcz-61a648a1)\n",
    "\n",
    "#### Want to Work Together?\n",
    "\n",
    "I hope this content is helpful to those that want to learn more about subsurface modeling, data analytics and machine learning. Students and working professionals are welcome to participate.\n",
    "\n",
    "* Want to invite me to visit your company for training, mentoring, project review, workflow design and / or consulting? I'd be happy to drop by and work with you! \n",
    "\n",
    "* Interested in partnering, supporting my graduate student research or my Subsurface Data Analytics and Machine Learning consortium (co-PIs including Profs. Foster, Torres-Verdin and van Oort)? My research combines data analytics, stochastic modeling and machine learning theory with practice to develop novel methods and workflows to add value. We are solving challenging subsurface problems!\n",
    "\n",
    "* I can be reached at mpyrcz@austin.utexas.edu.\n",
    "\n",
    "I'm always happy to discuss,\n",
    "\n",
    "*Michael*\n",
    "\n",
    "Michael Pyrcz, Ph.D., P.Eng. Associate Professor The Hildebrand Department of Petroleum and Geosystems Engineering, Bureau of Economic Geology, The Jackson School of Geosciences, The University of Texas at Austin\n",
    "\n",
    "#### More Resources Available at: [Twitter](https://twitter.com/geostatsguy) | [GitHub](https://github.com/GeostatsGuy) | [Website](http://michaelpyrcz.com) | [GoogleScholar](https://scholar.google.com/citations?user=QVZ20eQAAAAJ&hl=en&oi=ao) | [Book](https://www.amazon.com/Geostatistical-Reservoir-Modeling-Michael-Pyrcz/dp/0199731446) | [YouTube](https://www.youtube.com/channel/UCLqEr-xV-ceHdXXXrTId5ig)  | [LinkedIn](https://www.linkedin.com/in/michael-pyrcz-61a648a1)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "78deffb5",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
